package queue

import (
	"context"
	"sync"
	"sync/atomic"
	"unsafe"
)

// 用 channel 实现的 Cond，给 Cond 添加 WaitTimeout 方法，只支持 Broadcast，无法支持 Signal
type Cond struct {
	// 这把锁只是为了实现 Wait 方法的约定语义，不用于锁定内部资源
	L sync.Locker
	n unsafe.Pointer
}

func NewCond(l sync.Locker) *Cond {
	c := &Cond{L: l}
	n := make(chan struct{})
	// 直接赋值，初始化的时候没有并发问题
	c.n = unsafe.Pointer(&n)
	// atomic.StorePointer(&c.n, unsafe.Pointer(&n))
	return c
}

func (c *Cond) Wait() {
	n := c.NotifyChan()
	c.L.Unlock()
	<-n
	c.L.Lock()
}

func (c *Cond) WaitTimeout(ctx context.Context) error {
	n := c.NotifyChan()
	c.L.Unlock()
	select {
	case <-n:
		c.L.Lock()
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

// 这里的 chan 只有接收方，没有发送方，所以可以安全的关闭、替换 chan，
// 一旦引入 Signal 方法，就引入了发送方，关闭 chan 就可能导致 panic。
func (c *Cond) Broadcast() {
	n := make(chan struct{})
	ptrOld := atomic.SwapPointer(&c.n, unsafe.Pointer(&n))
	close(*((*chan struct{})(ptrOld)))
}

func (c *Cond) NotifyChan() <-chan struct{} {
	// 取值的时候会有并发问题，要原子操作
	ptr := atomic.LoadPointer(&c.n)
	return *((*chan struct{})(ptr))
}

type CondV2 struct {
	l sync.Locker
	c chan struct{}
}

func (c *CondV2) Wait() {
	c.l.Unlock()
	<-c.c
	c.l.Lock()
}
func (c *CondV2) WaitTimeout(ctx context.Context) error {
	c.l.Unlock()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case <-c.c:
		c.l.Lock()
		return nil
	}
}
func (c *CondV2) Signal() {
	select {
	case c.c <- struct{}{}:
	default:
	}
}
